#include "Config.h"

#include <Poco/Util/IniFileConfiguration.h>
#include <Poco/AutoPtr.h>
#include <Poco/Exception.h>
#include <fstream>
#include "server/net/NetworkControl.h"
#include "misc/Debug.h"

Poco::Mutex* Config::mutex = new( Poco::Mutex );
/*********************************************************/
Config& Config::Instance()
{
	mutex->lock();
	static Config pInstance;
	mutex->unlock();
	return( pInstance );
}
/*********************************************************/
Config::Config()
{
	Debug::Instance() << "Config :: Parsing Configuration file: config.ini" << std::endl;
	reparse();
}
/*********************************************************/
void Config::reparse()
{
	Debug& DEBUG = Debug::Instance();

	// Check if config.ini exists
	std::ifstream	ifile("config.ini");
	if ( ifile.is_open() ) {
		ifile.close();
	} else {
		writeDefaultConfig();
	}

	if ( !parse() ) {
		writeDefaultConfig();
		if ( !parse() ) {
			DEBUG << "Config :: Unable to parse configuration, default values corrupted or couldn't write default config.ini file to disk!" << std::endl;
			throw(std::logic_error("Unable to parse configuration, default values corrupted or couldn't write default config.ini file to disk!"));
		}
	}
}
/*********************************************************/
bool Config::parse()
{
	Debug& DEBUG = Debug::Instance();
	mutex->lock();
	Poco::AutoPtr<Poco::Util::IniFileConfiguration> pConf;
	try {
		// Parse the configuration file
		pConf = new Poco::Util::IniFileConfiguration("config.ini");

		/// Parse Middleware parameters
		// Get all required values
		pNetworkInterface = pConf->getString("Middleware.NetworkInterface");
		pNetworkMulticastGroup = pConf->getString("Middleware.NetworkMulticastGroup");
		pNetworkUnicastPortMin = pConf->getInt("Middleware.NetworkUnicastPortRangeMin");
		pNetworkUnicastPortMax = pConf->getInt("Middleware.NetworkUnicastPortRangeMax");
		pNameServerGroup = pConf->getString("Middleware.NameServerGroup");
		pNameServerDelay = pConf->getInt("Middleware.NameServerDelay");

		/// Parse Server parameters
		// Get all required values
		pMultithreadedJobExecution = pConf->getBool("Server.MultithreadedJobExecution");
		pRepository = pConf->getString("Server.DataRepository");
		pChunkSize = pConf->getInt("Server.ChunkSize");

		if ( pConf->hasOption("Server.MaximumArchiveSize")) {
			pMaxArchiveSize = pConf->getInt("Server.MaximumArchiveSize");
		} else {
			pMaxArchiveSize = DEFAULT_MAXARCHIVESIZE;
		}

		// Get the optionals/dependancies
		if ( pMultithreadedJobExecution ) {
			pThreadPoolThreshold = pConf->getDouble("Server.ThreadPoolThreshold");
			pThreadPoolMin = pConf->getInt("Server.ThreadPoolMinThreads");
		}

		// Get the optionals 
		if ( pConf->hasOption("Server.UIEcho") ) {
			pUIEcho = pConf->getBool("Server.UIEcho");
		} else {
			pUIEcho = true;
		}

		if ( pConf->hasOption("Server.WriteDumpLog") ) {
			pDumpLog = pConf->getString("Server.WriteDumpLog");
		} else {
			pDumpLog = "";
		}

		if ( pConf->hasOption("Middleware.RingName")) {
			pRingName = pConf->getString("Middleware.RingName");
		} else {
			pRingName = "";
		}
			

		mutex->unlock();
		return( true );
	} catch(Poco::NotFoundException& e ) {
		DEBUG << "Config :: Missing some Configuration parameters in config.ini!" << std::endl;
		mutex->unlock();
		return( false );
	}
}
/*********************************************************/
void Config::writeDefaultConfig()
{
	mutex->lock();
	std::ofstream file;
	file.open("config.ini");
	file << "; Middleware configuration parameters" << std::endl << std::endl;
	file << "[Middleware]" << std::endl;
	file << "NetworkInterface = "<< NetworkControl::getNetworkInterface() << std::endl;
	file << "NetworkMulticastGroup = " << DEFAULT_MULTICAST_GROUP << std::endl;
	file << "NameServerGroup = " << DEFAULT_NAMESERVER_GROUP << std::endl;
	file << "NameServerDelay = " << DEFAULT_NAMESERVER_DELAY << std::endl;
	file << "NetworkUnicastPortRangeMin = " << DEFAULT_UNICAST_PORT_MIN << std::endl;
	file << "NetworkUnicastPortRangeMax = " << DEFAULT_UNICAST_PORT_MAX << std::endl;
	file << "RingName = " << DEFAULT_RING_NAME << std::endl << std::endl;
	file << "[Server]" << std::endl;
	file << "MultithreadedJobExecution = true" << std::endl;
	file << "ThreadPoolThreshold = 0.25" << std::endl;
	file << "ThreadPoolMinThreads = 5" << std::endl;
	file << "UIEcho = true" << std::endl;
	file << "DataRepository = repo" << std::endl;
	file << "WriteDumpLog = log.txt" << std::endl;
	file << "ChunkSize = " << DEFAULT_CHUNKSIZE << std::endl;
	file.close();
	mutex->unlock();
}
/*********************************************************/
void Config::setRingPort(const unsigned int& p)
{
	mutex->lock();
	pRingPort = p;
	mutex->unlock();
}
/*********************************************************/
unsigned int Config::getRingPort() const
{
	mutex->lock();
	unsigned int port = pRingPort;
	mutex->unlock();
	return port;
}
/*********************************************************/
void Config::setNameServerDelay(const unsigned int& d)
{
	mutex->lock();
	pNameServerDelay = d;
	mutex->unlock();
}
/*********************************************************/
unsigned int Config::getNameServerDelay() const
{
	mutex->lock();
	unsigned int d = pNameServerDelay;
	mutex->unlock();
	return d;
}
/*********************************************************/
void Config::setRingName(const std::string& ring)
{
	mutex->lock();
	pRingName = ring;
	mutex->unlock();
}
/*********************************************************/
std::string Config::getRingName() const
{
	mutex->lock();
	std::string name = pRingName;
	mutex->unlock();
	return name;
}
/*********************************************************/
void Config::setNameServerGroup(const std::string& group)
{
	mutex->lock();
	pNameServerGroup = group;
	mutex->unlock();
}
/*********************************************************/
std::string Config::getNameServerGroup() const
{
	mutex->lock();
	std::string group = pNameServerGroup;
	mutex->unlock();
	return group;
}
/*********************************************************/
void Config::setWriteDumpLog(const std::string& dump)
{
	mutex->lock();
	pDumpLog = dump;
	mutex->unlock();
}
/*********************************************************/
std::string Config::getWriteDumpLog() const
{
	mutex->lock();
	std::string temp = pDumpLog;
	mutex->unlock();
	return temp;
}
/*********************************************************/
unsigned int Config::getNetworkUnicastPortRangeMin() const
{
	mutex->lock();
	unsigned int port = pNetworkUnicastPortMin;
	mutex->unlock();
	return port;
}
/*********************************************************/
void Config::setNetworkUnicastPortRangeMin(const unsigned int& p)
{
	mutex->lock();
	pNetworkUnicastPortMin = p;
	mutex->unlock();
}
/*********************************************************/
unsigned int Config::getNetworkUnicastPortRangeMax() const
{
	mutex->lock();
	unsigned int port = pNetworkUnicastPortMax;
	mutex->unlock();
	return port;
}
/*********************************************************/
void Config::setNetworkUnicastPortRangeMax(const unsigned int& p)
{
	mutex->lock();
	pNetworkUnicastPortMax = p;
	mutex->unlock();
}
/*********************************************************/
void Config::setMaxArchiveSize(const unsigned long& maxArchSize) {
	mutex->lock();
	pMaxArchiveSize = maxArchSize;
	mutex->unlock();
}
/*********************************************************/
void Config::setChunkSize(const unsigned long& chunkSize) {
	mutex->lock();
	pChunkSize = chunkSize;
	mutex->unlock();
}
/*********************************************************/
unsigned long Config::getChunkSize() const
{
	mutex->lock();
	unsigned long chunksize = pChunkSize;
	mutex->unlock();
	return( chunksize );
}
/*********************************************************/
unsigned long Config::getMaxArchiveSize() const
{
	mutex->lock();
	unsigned long maxArchSize = pMaxArchiveSize;
	mutex->unlock();
	return( maxArchSize );
}
/*********************************************************/
std::string Config::getFileRepository() const
{
	mutex->lock();
	std::string str = pRepository;
	mutex->unlock();
	return( str );
}
/*********************************************************/
void Config::setFileRepository(const std::string& repo)
{
	mutex->lock();
	pRepository = repo;
	mutex->unlock();
}
/*********************************************************/
std::string Config::getQuitEvent() const
{
	mutex->lock();
	std::string str = pQuitEvent;
	mutex->unlock();
	return( str );
}
/*********************************************************/
void Config::setQuitEvent(const std::string& str)
{
	mutex->lock();
	pQuitEvent = str;
	mutex->unlock();
}
/*********************************************************/
bool Config::getUIEcho() const
{
	mutex->lock();
	bool echo = pUIEcho;
	mutex->unlock();
	return( echo );
}
/*********************************************************/
void Config::setUIEcho(const bool echo)
{
	mutex->lock();
	pUIEcho = echo;
	mutex->unlock();
}
/*********************************************************/
float Config::getThreadPoolThreshold() const
{
	mutex->lock();
	float threshold = pThreadPoolThreshold;
	mutex->unlock();
	return (threshold);
}
/*********************************************************/
int Config::getThreadPoolMin() const
{
	mutex->lock();
	unsigned int min = pThreadPoolMin;
	mutex->unlock();
	return( min );
}
/*********************************************************/
void Config::setThreadPoolThreshold(const float threshold)
{
	mutex->lock();
	pThreadPoolThreshold = threshold;
	mutex->unlock();
}
/*********************************************************/
void Config::setThreadPoolMin(const unsigned int min)
{
	mutex->lock();
	pThreadPoolMin = min;
	mutex->unlock();
}
/*********************************************************/
std::string Config::getNetworkMulticastGroup() const
{
	mutex->lock();
	std::string mGroup = pNetworkMulticastGroup;
	mutex->unlock();
	return( mGroup );
}
/*********************************************************/
std::string Config::getNetworkInterface() const
{
	mutex->lock();
	std::string interface = pNetworkInterface;
	mutex->unlock();
	return( interface );
}
/*********************************************************/
bool Config::getMultithreadedJobExecution() const
{
	mutex->lock();
	bool mt = pMultithreadedJobExecution;
	mutex->unlock();
	return( mt );
}
/*********************************************************/
void Config::setMultithreadedJobExecution(const bool mt)
{
	mutex->lock();
	pMultithreadedJobExecution = mt;
	mutex->unlock();
}
/*********************************************************/
void Config::setUnicastTrafficEvent(const std::string& event)
{
	mutex->lock();
	pUnicastTrafficEvent = event;
	mutex->unlock();
}
/*********************************************************/
void Config::setMulticastTrafficEvent(const std::string& event)
{
	mutex->lock();
	pMulticastTrafficEvent = event;
	mutex->unlock();
}
/*********************************************************/
void Config::setActivateProcessingEvent(const std::string& event)
{
	mutex->lock();
	pActivateProcessingEvent = event;
	mutex->unlock();
}
/*********************************************************/
void Config::setInterfaceInputEvent(const std::string& event)
{
	mutex->lock();
	pInterfaceInputEvent = event;
	mutex->unlock();
}
/*********************************************************/
std::string Config::getUnicastTrafficEvent() const
{
	mutex->lock();
	std::string event = pUnicastTrafficEvent;
	mutex->unlock();
	return( event );
}
/*********************************************************/
std::string Config::getMulticastTrafficEvent() const
{
	mutex->lock();
	std::string event = pMulticastTrafficEvent;
	mutex->unlock();
	return( event );
}
/*********************************************************/
std::string Config::getActivateProcessingEvent() const
{
	mutex->lock();
	std::string event = pActivateProcessingEvent;
	mutex->unlock();
	return( event );
}
/*********************************************************/
std::string Config::getInterfaceInputEvent() const
{
	mutex->lock();
	std::string event = pInterfaceInputEvent;
	mutex->unlock();
	return( event );
}
/*********************************************************/
void Config::setNetworkMulticastGroup(const std::string& addr)
{
	mutex->lock();
	pNetworkMulticastGroup = addr;
	mutex->unlock();
}
/*********************************************************/
void Config::setNetworkInterface(const std::string& interface)
{
	mutex->lock();
	pNetworkInterface = interface;
	mutex->unlock();
}
/*********************************************************/
Config::~Config()
{
	delete( mutex );
}
/*********************************************************/
